package ycl.security.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import ycl.security.handler.*;

import javax.annotation.Resource;

/**
 * 登录认证
 *
 * @author: ycl
 * @date 2022-03-08 19:55:40
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
@ComponentScan({"ycl"})
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Resource
	private AuthSuccessHandler authSuccessHandler;
	@Resource
	private AuthFailureHandler authFailureHandler;
	@Resource
	private AuthLogoutHandler authLogoutHandler;
	@Resource
	private AuthLogoutSuccessHandler authLogoutSuccessHandler;
	@Resource
	private AuthEntryPoint authEntryPoint;
	@Resource
	private AuthAccessDeniedHandler authAccessDeniedHandler;
	@Resource
	private AuthUserDetailProvider authUserDetailProvider;
	@Resource
	private AuthTokenFilter authTokenFilter;

	@Override
	protected void configure(AuthenticationManagerBuilder auth) {
		auth.authenticationProvider(authUserDetailProvider);
	}

	/**
	 * 配置 web 请求安全性
	 *
	 * @param web 前端请求
	 */
	@Override
	public void configure(WebSecurity web) {
		//放行特例资源
		String[] paths = {
				"/assets/**",
				"/css/**",
				"/swagger-ui.html",
				"/webjars/**",
				"/v2/**",
				"/swagger-resources/**",
				"/images/**",

				//登录
				"/login/**",
		};
		web.ignoring()
				.antMatchers(paths);
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new PasswordEncoder() {
			/**
			 * 加密密码
			 * @param charSequence 密码
			 * @return 加密后的密码
			 */
			@Override
			public String encode(CharSequence charSequence) {
				return (String) charSequence;
			}

			/**
			 * 校验密码
			 * @param charSequence 加密密码
			 * @param s 原密码
			 * @return 两个密码是否匹配
			 */
			@Override
			public boolean matches(CharSequence charSequence, String s) {
				return charSequence.equals(s);
			}
		};
	}


	/**
	 * 配置 http 请求安全性
	 *
	 * @param http http请求
	 * @throws Exception
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {

		//配置不需要权限访问的路径
		String[] ignorePath = {
				"/ycl-security/login/*",
		};
		http.csrf().disable().authorizeRequests()
				//.antMatchers("/v2/api-docs").permitAll()
				//配置不校验的路径
				.mvcMatchers(ignorePath).permitAll()
				.anyRequest().fullyAuthenticated()
				.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

				.and().formLogin()
				//配置登录入口
				.loginProcessingUrl("/login")
				//登录成功回调
				.successHandler(authSuccessHandler)
				//登录失败回调
				.failureHandler(authFailureHandler)

				.and().logout()
				//退出登录
				.addLogoutHandler(authLogoutHandler)
				//退出成功
				.logoutSuccessHandler(authLogoutSuccessHandler)

				//认证失败处理
				.and().exceptionHandling()
				//未登录
				.authenticationEntryPoint(authEntryPoint)
				//未授权
				.accessDeniedHandler(authAccessDeniedHandler);

		http.addFilterAt(authTokenFilter, UsernamePasswordAuthenticationFilter.class);
		http.headers().cacheControl();
	}
}
